#include <zest.hpp>
#include <string.h>
#include <DRAMImageStream.hpp>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
using namespace std;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
uint64_t StatFileSize( const char * file_name ) {
    struct stat st;

    if( stat( file_name, &st ) < 0 ) {
        cout << "ERROR: Can't stat file " << file_name << endl;
        return 0ULL;
    }
    
    return st.st_size;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
bool StatFile::open( const char * file_name, const bool sync ) {
    
    nsize = StatFileSize( file_name );
    fsize = float( nsize );
    
    if( sync ) {
        fd = ::open( file_name, O_RDONLY | O_SYNC );
    }
    else {
        fd = ::open( file_name, O_RDONLY );
    }
    
    if( fd < 0 ) {
        cout << "ERROR: Can't open file " << file_name << endl;
        nsize = 0ULL;
        fsize = 0.0;
        return false;
    }
    return true;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
bool IFStreamProgress::open( const char * file_name, pthread_mutex_t * _disklock, const bool sync ) {
    
    disklock = _disklock;

    nsize = StatFileSize( file_name );
    fsize = float( nsize );

    ifstream::open( file_name, ios::binary | ios::in );
    
    if( !is_open() ) {
        cout << "wtf wtf" << endl;
        return false;
    }

    bytesOut = 0ULL;
    return true;
}
size_t IFStreamProgress::ReadAndCountBytes( char * buf, size_t len ) {
    if( disklock ) {
        pthread_mutex_lock( disklock );
    }
    read( buf, len );
    if( disklock ) {
        pthread_mutex_unlock( disklock );
    }
    return gcount();
}
bool IFStreamProgress::ReadTrackProgress( char * dst, size_t len ) {
    
    if( bytesOut == nsize ) {
        // eof
        return false;
    }

    bytesOut += ReadAndCountBytes( dst, len );
    return true;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
bool BufferedIFStreamProgress::open( const char * file_name, pthread_mutex_t * _disklock, const bool sync ) {
    
    disklock = _disklock;
    if( !StatFile::open( file_name, sync ) ) {
        return false;
    }
    
    if( strcmp( file_name, "/dev/null" ) == 0 ) {
        bufferwarn = false;
    }
    else {
        bufferwarn = true;
    }

    bytesOut = 0ULL;
    bytesIn  = 0ULL;
    
    BufferIn();
    
    return true;
}
size_t BufferedIFStreamProgress::ReadAndCountBytes( char * buf, size_t len ) {
    if( disklock ) {
        pthread_mutex_lock( disklock );
    }
    size_t n = read( buf, len );
    if( disklock ) {
        pthread_mutex_unlock( disklock );
    }
    return n;
}
size_t BufferedIFStreamProgress::BufferIn( void ) {
    
    // verify that there is still something remaining to be read in
    if( bufferwarn ) {
        // this is turned off if the file name was /dev/null
        WARN( bytesIn < nsize, "-- this should not happen --" );
    }
    
    // reset buffer pointer and read
    pbuf = &buf[0];
    const size_t bytes = ReadAndCountBytes( pbuf, buffsize );
    
    // track bytes buffered in
    bytesIn += bytes;

    // reset buffer position and end index
    pos = 0;
    max = bytes;
    
    return bytes;
}
bool BufferedIFStreamProgress::ReadTrackProgress( char * dst, size_t len ) {
    
    if( bytesOut == nsize ) {
        // eof
        return false;
    }
    
    if( pos >= max ) {
        // buffer pos advanced beyond end of buffer
        // ... re-buffer
        BufferIn();
    }
    if( len + pos > max ) {
        // handle corner case

        // number of valid bytes in the buffer
        const size_t nvalid = max - pos;

        // zero out the destination
        // copy in the valid bytes
        memset( dst, 0, len );
        memcpy( dst, pbuf, nvalid );

        bytesOut += nvalid;
        
        // on eof:
        // ... valid data was read on this call: return true
        // ... don't want to call BufferIn() nor recurse
        if( bytesOut == nsize )  { return true;  }
        
        // fell through to here:
        // ... need to BufferIn() because more data is still availble
        // ... recursing is ok for same reason
        BufferIn();
        return ReadTrackProgress( dst+nvalid, len-nvalid );
    }
    
    // copy to the destination
    memcpy( dst, pbuf, len );
    
    pos      += len;  // position in buffer
    pbuf     += len;  // advance buffer ptr
    bytesOut += len;  // total bytes read out
    
    return true;
}









